﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// YPbPr Channel
    /// </summary>
    public enum YPbPrChannel
    {
        /// <summary>
        /// 
        /// </summary>
        Y = 0,
        /// <summary>
        /// 
        /// </summary>
        Pb = 1,
        /// <summary>
        /// 
        /// </summary>
        pr = 2
    }

    /// <summary>
    /// Class to convert RGB to YPbPr and back
    /// </summary>
    [DebuggerDisplay("Y: {m_Y}, Pb: {m_Pb}, Pr: {m_Pr}")]
    public class YPbPr : AbstractColorSpace
    {
        protected double m_Y = 0.0f;
        protected double m_Pb = 0.0f;
        protected double m_Pr = 0.0f;

        /// <summary>
        /// Primary constructor
        /// </summary>
        /// <param name="_color"></param>
        public YPbPr(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="YPbPr"/> class.
        /// </summary>
        /// <param name="_Y">The _ Y.</param>
        /// <param name="_Pb">The _ pb.</param>
        /// <param name="_Pr">The _ pr.</param>
        public YPbPr(int _Y, int _Pb, int _Pr)
        {
            m_Y = _Y;
            m_Pb = _Pb;
            m_Pr = _Pr;
        }
        /// <summary>
        /// Primary constructor/also used by clone to create a deep copy
        /// </summary>
        /// <param name="_ypbpr"></param>
        public YPbPr(YPbPr _ypbpr)
        {
            this.m_Color = _ypbpr.m_Color;
            this.m_Y = _ypbpr.m_Y;
            this.m_Pb = _ypbpr.m_Pb;
            this.m_Pr = _ypbpr.m_Pr;
        }

        /// <summary>
        /// Convert
        /// </summary>
        protected override void CalcValues()
        {
            m_Y = 0.299f * m_Color.R + 0.587f * m_Color.G + 0.114f * m_Color.B;
            m_Pb = -0.168736 * m_Color.R - 0.331264f * m_Color.G + 0.5f * m_Color.B;
            m_Pr = 0.5f * m_Color.R - 0.418688 * m_Color.G - 0.081312 * m_Color.B;
        }

        /// <summary>
        /// Gets or sets Y
        /// </summary>
        public double Y
        {
            get { return m_Y; }
            set { m_Y = value; }
        }

        /// <summary>
        /// Gets or sets Pb
        /// </summary>
        public double Pb
        {
            get { return m_Pb; }
            set { m_Pb = value; }
        }

        /// <summary>
        /// Gets or sets Pr
        /// </summary>
        public double Pr
        {
            get { return m_Pr; }
            set { m_Pr = value; }
        }

        //TODO: correct formula: found on http://www.equasys.de/colorconversion.html#YPbPr-RGBColorFormatConversion
        /// <summary>
        /// Recalculate RGB
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            double R = ( 298.082f * m_Y + 408.583f * m_Pr ) / 256.0f - 222.921f;
            double G = (298.082f * m_Y - 100.291f * m_Pb - 208.120 * m_Pr) / 256.0f + 135.576f;
            double B = (298.082f * m_Y + 516.412f * m_Pb) / 256.0f - 276.836f;
            return Color.FromArgb((int)R, (int)G, (int)B);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color Y = {0}, Pb = {1}, Pr = {2}", m_Y, m_Pb, m_Pr);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_Y, m_Pb, m_Pr };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            this.m_Y = (double)_aObject[0];
            this.m_Pb = (double)_aObject[1];
            this.m_Pr = (double)_aObject[2];
        }

        /// <summary>
        /// Create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new YPbPr(this);
        }
    }
}
